#ifndef __CONNECTION_included
#define __CONNECTION_included

#include <exception>
#include <string>
#include <boost/shared_ptr.hpp>
#include <boost/exception/all.hpp>
#include <boost/thread.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/thread/condition.hpp>

#include "PackageReceiver.h"
#include "../engine/Request.h"
#include "../engine/Response.h"
#include "../client-common/Config.h"
#include "Channel.h"
#include "Task.h"

using namespace std;
using namespace Engine;

class CConnectionException : public virtual std::exception, public virtual boost::exception {};
typedef boost::error_info<struct tag_connection_error, string> connection_error;

class CPeerConfig {

public :

	string mDeviceId;
	string mSessionId;
	string mToken;

	CPeerConfig() { 
		Client::Common::CConfig& conf = Client::Common::CConfig::GetInstance();
		mDeviceId = conf.mDeviceId;
	}

	CPeerConfig(CPeerConfig& cfg) {
		mDeviceId = cfg.mDeviceId;
		mSessionId = cfg.mSessionId;
		mToken = cfg.mToken;
	}
};

class CConnection : public IPackageReceiver {

public :
	
	CConnection(string& addr, int port, ChannelRef ch) :
		mRemoteAddr(addr),
		mRemotePort(port),
		mChannel(ch),
		mClosed(false)
	{
		
	}

	CConnection(string& addr, int port, ChannelRef ch, CPeerConfig& cfg) :
		mRemoteAddr(addr),
		mRemotePort(port),
		mChannel(ch),
		mConfig(cfg),
		mClosed(false)
	{
		
	}

	virtual ~CConnection() { }

	CPeerConfig& GetPeerConfig() { return mConfig; }

	void SendResponse(ResponseRef resp);
	ResponseRef SendRequest(RequestRef r, int try_count);

	virtual bool IsControlConnection() { return false; }
	virtual ResponseRef SendRequest(RequestRef r) {
		return SendRequest(r, 3);
	}

	void SetResponse(boost::shared_ptr<CResponse> resp);

	void Start() {
		mRecvThread = boost::shared_ptr<boost::thread>(new boost::thread(boost::bind(&CConnection::RecvLoop, this)));
	}

	virtual void AddTask(TaskRef t) { }

	virtual void OnReceive(PackageRef p);
	virtual void Setup() = 0;
	virtual void Close();

protected :

	string mRemoteAddr;
	int mRemotePort;
	ChannelRef mChannel;
	CPeerConfig mConfig;
	bool mClosed;

private :

	RequestRef mRequest;
	ResponseRef mResponse;
	boost::mutex mSendMutex;
	boost::mutex mWaitMutex;
	boost::condition_variable_any mWaitCond;
	boost::shared_ptr<boost::thread> mRecvThread;
	
	PackageRef ReceivePackage();
	void WaitForResponse();
	void AddAuthorization(RequestRef request, ResponseRef response);

	void RecvLoop() {
		DebugStream << "Receive thread start ... \r\n";
		mChannel->ScheduleReceive(shared_from_this());
	}
};

typedef boost::shared_ptr<CConnection> ConnectionRef;

#endif //__CONNECTION_included
